/*
 * Copyright (c) 2009 John Pritchard, WTKX Project Group
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package wtkx.in;

import wtkx.Alert;
import wtkx.Application;
import wtkx.Button;
import wtkx.Component;
import wtkx.Scene;
import wtkx.Textarea;
import wtkx.Window;
import wtkx.co.Sequence;
import wtkx.io.WTKX;
import wtkx.ui.Mouse;

import jbxml.Function;
import jbxml.Dictionary;

import java.io.InputStream;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * Script application loader.
 *
 * @author jdp
 */
public abstract class Script
    implements Application
{

    /*
     * [TODO:Review] This is it for the moment.. 
     */
    public final static URL IconLockedFalse;
    public final static URL IconLockedTrue;
    public final static URL IconNetworkOffline;
    public final static URL IconNetworkPing;
    public final static URL IconNetworkTransmit;
    public final static URL IconRobotStart;
    public final static URL IconRobotStop;
    public final static URL IconUserAway;
    public final static URL IconUserBusy;
    public final static URL IconUserOffline;
    public final static URL IconUserOnline;
    public final static URL IconTrashFull;
    public final static URL IconNavUp;
    public final static URL IconNavDown;
    public final static URL IconNavPrevious;
    public final static URL IconNavNext;
    public final static URL IconReload;
    public final static URL IconCallStart;
    public final static URL IconCallStop;
    static {
        Class clas = Application.class;
        IconLockedFalse = clas.getResource("/authenticated-false-24x24.png");
        IconLockedTrue = clas.getResource("/authenticated-true-24x24.png");
        IconNetworkOffline = clas.getResource("/network-offline-24x24.png");
        IconNetworkPing = clas.getResource("/network-ping-24x24.png");
        IconNetworkTransmit = clas.getResource("/network-transmit-24x24.png");
        IconRobotStart = clas.getResource("/robot-start-24x24.png");
        IconRobotStop = clas.getResource("/robot-stop-24x24.png");
        IconUserAway = clas.getResource("/user-away-24x24.png");
        IconUserBusy = clas.getResource("/user-busy-24x24.png");
        IconUserOffline = clas.getResource("/user-offline-24x24.png");
        IconUserOnline = clas.getResource("/user-online-24x24.png");
        IconTrashFull = clas.getResource("/user-trash-full-24x24.png");
        IconNavUp = clas.getResource("/up-24x24.png");
        IconNavDown = clas.getResource("/down-24x24.png");
        IconNavPrevious = clas.getResource("/previous-24x24.png");
        IconNavNext = clas.getResource("/next-24x24.png");
        IconReload = clas.getResource("/reload-24x24.png");
        IconCallStart = clas.getResource("/call-start-24x24.png");
        IconCallStop = clas.getResource("/call-stop-24x24.png");
    }
    public final static URL[] Icons = {
        IconLockedFalse,
        IconLockedTrue,
        IconNetworkOffline,
        IconNetworkPing,
        IconNetworkTransmit,
        IconRobotStart,
        IconRobotStop,
        IconUserAway,
        IconUserBusy,
        IconUserOffline,
        IconUserOnline,
        IconTrashFull,
        IconNavUp,
        IconNavDown,
        IconNavPrevious,
        IconNavNext,
        IconReload,
        IconCallStart,
        IconCallStop
    };

	protected Window window;

    protected Scene scene;

    protected Dictionary<String,String> properties;

    protected boolean isApplet;

    protected Class<? extends Application> chainNext;

    public Script(){
        super();
    }
    protected Script(Class<? extends Application> next){
        super();
        this.chainNext = next;
    }


    protected URL sourceFrom(String path)
		throws java.io.IOException 
    {
        URL source = new URL(path);

        return source;
    }
    protected URL sourceFrom(Dictionary<String, String> properties)
		throws java.io.IOException 
    {
		if (!properties.containsKey(Application.PARAM))
			throw new IllegalArgumentException("Missing parameter '"+Application.PARAM+"'.");

        else {
            String path = properties.get(Application.PARAM);

            URL source = this.sourceFrom(path);

            return source;
        }
    }
	public void startup(Scene scene, Dictionary<String, String> properties)
		throws java.io.IOException 
    {
        this.scene = scene;
        this.properties = properties;

        URL source = this.sourceFrom(properties);

        this.window = scene.open(source);

        this.window.init(this);
	}
    public final void startup(Component component)
		throws java.io.IOException
    {
        Scene scene = component.getScene();
        if (null != scene){
            this.scene = scene;
            Dictionary<String,String> properties = component.getProperties();
            if (null != properties){
                this.properties = properties;
                this.startup(scene,properties);
            }
            else
                throw new IllegalStateException("Missing 'properties' on parent component.");
        }
        else
            throw new IllegalStateException("Missing 'scene' on parent component.");
    }
    public final void startup(Script parent)
		throws java.io.IOException
    {        
        Scene scene = parent.scene;
        if (null != scene){
            this.scene = scene;
            Dictionary<String,String> properties = parent.properties;
            if (null != properties){
                this.properties = properties;
                this.startup(scene,properties);
            }
            else
                throw new IllegalStateException("Missing 'properties' on parent script.");
        }
        else
            throw new IllegalStateException("Missing 'scene' on parent script.");
    }
	public void shutdown()
        throws java.io.IOException
    {
        Window window = this.window;
        if (null != window){
            this.window = null;
            window.close();
            window.destroy();
        }
	}
	public void resume(){
	}
	public void suspend(){
	}
    public boolean print(Component component){
        Function function = Function.Get();
        System.out.printf("Function id='%s' @ '%s'\n",function.getId(),component);
        return true;
    }
    public boolean printClick(Component component, Mouse.Button button, float x, float y){
        Function function = Function.Get();
        System.out.printf("Function id='%s' @ '%s' (%f,%f)\n",function.getId(),component,x,y);
        return true;
    }
    public boolean printPress(Component component, char ch){
        Function function = Function.Get();
        System.out.printf("Function id='%s' @ '%s' (%c)\n",function.getId(),component,ch);
        return true;
    }
    public boolean systemExit(Component component, Mouse.Button button, float x, float y){
        if (!this.isApplet){
            System.exit(0);
        }
        return true;
    }
    public boolean reload(Component component, Mouse.Button button, float x, float y){
        if (component instanceof Button){
            try {
                this.shutdown();

                this.startup(this.scene,this.properties);
            }
            catch (IOException exc){
                exc.printStackTrace();
            }
        }
        else if (component instanceof Textarea)
            try {
                ((Textarea)component).reload();
            }
            catch (Function.Error ferr){
                component.alertBroken(ferr);
            }

        return true;
    }
    public boolean close(Component component, Mouse.Button button, float x, float y){

        this.closeChain();

        Window window = component.getWindow();
        if (window == this.window)
            try {
                this.shutdown();
            }
            catch (Exception exc){
                exc.printStackTrace();
            }
        else
            window.close();

        return true;
    }
    public boolean alert(Component component, Mouse.Button button, float x, float y){
        
        component.alertGo("Alert");

        return true;
    }
    public boolean save(Component component, Mouse.Button button, float x, float y){
        
        if (component instanceof Textarea)
            try {
                ((Textarea)component).save();
            }
            catch (Function.Error ferr){
                component.alertBroken(ferr);
            }

        return true;
    }
    public boolean debug(Component component, Mouse.Button button, float x, float y){
        
        if (component instanceof Button)
            component = component.getWindow();

        component.borderDebug();
        component.invalidate();

        return true;
    }
    public boolean focus(Component component){
        
        component.requestFocus();

        return true;
    }
    protected void closeChain(){

        if (null != this.chainNext){
            try {
                Application application = this.chainNext.newInstance();
                application.startup(this.scene,this.properties);
            }
            catch (Exception exc){
                exc.printStackTrace();
            }
        }
    }
    public boolean hasChainNext(){
        return (null != this.chainNext);
    }
    public Class getChainNext(){
        return this.chainNext;
    }
    public void setChainNext(Class<? extends Application> clas){
        this.chainNext = clas;
    }
    public void sceneUnique(){
        Scene scene = this.scene;
        if (null != scene){
            /*
             * drop applications not the first
             */
            Sequence<Component> droplist = scene.unique();

            for (Component droppingC : droplist){

                Application droppingA = droppingC.getApplication();
                if (null != droppingA)
                    try {
                        droppingA.shutdown();
                    }
                    catch (Exception exc){
                        exc.printStackTrace();
                    }

                try {
                    scene.remove(droppingC);
                }
                catch (java.util.NoSuchElementException correct){
                }
                catch (Exception exc){
                    exc.printStackTrace();
                }
                try {
                    droppingC.destroy();
                }
                catch (Exception exc){
                    exc.printStackTrace();
                }
            }
        }
    }

    public final Alert alertStop(String message){
        return this.window.alertStop(message);
    }
    public final Alert alertStop(Throwable thrown){
        return this.window.alertStop(thrown);
    }
    public final Alert alertGo(String message){
        return this.alertGo(message);
    }
    public final Alert alertGo(Throwable thrown){
        return this.window.alertGo(thrown);
    }
    public final Alert alertTimeout(String message){
        return this.window.alertTimeout(message);
    }
    public final Alert alertTimeout(Throwable thrown){
        return this.window.alertTimeout(thrown);
    }
    public final Alert alertBroken(String message){
        return this.alertBroken(message);
    }
    public final Alert alertBroken(Throwable thrown){
        return this.alertBroken(thrown);
    }    
    public final Window updateOpen(String id, String path)
        throws IOException
    {
        Window window = this.window.open(id,path);
        window.init(this);
        return window;
    }
    public final Window updateOpen(String id, URL url)
        throws IOException
    {
        Window window = this.window.open(id,url);
        window.init(this);
        return window;
    }
    public final Window updateOpen(String id, InputStream content)
        throws IOException
    {
        Window window = this.window.open(id,content);
        window.init(this);
        return window;
    }
    public final void updateClose(String id){
        this.window.close(id);
    }
    public final Component updateAppend(String id, String at, String with){
        return this.window.updateAppend(id,at,with);
    }
    public final Component updateInsert(String id, String at, int idx, String with){
        return this.window.updateInsert(id,at,idx,with);
    }
    public final Component updateReplace(String id, String at, String search, String with){
        return this.window.updateReplace(id,at,search,with);
    }
    public final Component updateDelete(String id, String at, int start, int end){
        return this.window.updateDelete(id,at,start,end);
    }

}
